package org.dfl.core.model.transitions;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.dfl.core.data.types.Type;
import org.dfl.core.data.types.TypeContainer;
import org.dfl.core.model.TInputEdge;
import org.dfl.core.model.TransitionExecutionException;
import org.dfl.messages.Messages;

/**
 * transition that has only one input
 * @author Piotr Wlodarczyk
 *
 */
public abstract class OneInputTransition extends Transition {
	private TInputEdge inputEdge;
	private TypeContainer inputContainer;
	
	public List<String> getFreeInputs(Type type) {
		if(inputEdge != null){
			return new ArrayList<String>();
		}
		return null;
	}
	
	@Override
	protected void init() {
		super.init();

		// add inputs and outputs definitions
		setOutputType(getDefaultOutputDefinition());
		inputContainer = new TypeContainer(getDefaultInputDefinition());
		
		// polaczenie typu wejscia z wyjsciem
		connectInputAndOutput();
	}
	
	protected abstract void connectInputAndOutput();
	protected abstract Type getDefaultInputDefinition();
	protected abstract Type getDefaultOutputDefinition();
	protected abstract Object fireSingle(Object input) throws TransitionExecutionException ;
	
	public boolean isInputNameNeeded() {
		return false;
	}
	
	public Collection<TInputEdge> getInputEdges() {
		Collection<TInputEdge> c = new ArrayList<TInputEdge>();
		if(inputEdge!=null){
			c.add(inputEdge);
		}
		return c;
	}
	
	public void addInput(TInputEdge e) throws Exception {
		// first check availability of inputs
		if(inputEdge!=null){
			throw new Exception(Messages.OneInputTransition_errorNoEmptyInput);
		}
		// then connect the types
		if(!e.getTypeContainer().connectWith(inputContainer)){
			throw new Exception(Messages.Transition_errorInputType);
		}
		inputEdge = e;
	}
	
	@Override
	protected final Object fire(Map<String, Object> inputMap) throws TransitionExecutionException {
		return fireSingle(inputMap.get(inputEdge.getLabel()));
	}

	protected TypeContainer getInputContainer() {
		return inputContainer;
	}


	protected TInputEdge getInputEdge() {
		return inputEdge;
	}
	
	@Override
	public void disconnectAllInputs() {
		if(getInputEdge()!=null){
			getInputEdge().disconnect();
		}
	}

	protected void setInputEdge(TInputEdge inputEdge) {
		this.inputEdge = inputEdge;
	}

	@Override
	protected String getInputDescription() {
		return getInputContainer().getType().toString();
	}

	public boolean areAllInputsDefined() {
		return inputEdge!=null;
	}

	public int inputsCount() {
		return 1;
	}

	public void removeInput(String key) {
		inputEdge = null;
	}

	@Override
	protected boolean hasFreeInput(String key) {
		return inputEdge==null;
	}
	
	public boolean hasFreeInput(Type type) {
		return (inputEdge==null && type.canMerge(this.getInputContainer().getType()));
	}
}
